import { css } from '@emotion/css';
import { DOMAttributes } from '@react-types/shared';
import { memo, forwardRef, useCallback, useState, useEffect, useRef } from 'react';
import React from 'react';
import { useLocation, useNavigate } from 'react-router-dom-v5-compat';

import { GrafanaTheme2, NavModelItem } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { t } from '@grafana/i18n';
import { config, reportInteraction } from '@grafana/runtime';
import { ScrollContainer, useStyles2 } from '@grafana/ui';
import { useGrafana } from 'app/core/context/GrafanaContext';
import { setBookmark } from 'app/core/reducers/navBarTree';
import { usePatchUserPreferencesMutation } from 'app/features/preferences/api/index';
import { useDispatch, useSelector } from 'app/types/store';

import { InviteUserButton } from '../../InviteUserButton/InviteUserButton';
import { shouldRenderInviteUserButton } from '../../InviteUserButton/utils';

import { MegaMenuHeader } from './MegaMenuHeader';
import { MegaMenuItem } from './MegaMenuItem';
import { usePinnedItems } from './hooks';
import { enrichWithInteractionTracking, findByUrl, getActiveItemByUrl } from './utils';
import { backendSrv } from 'app/core/services/backend_srv';

export const MENU_WIDTH = '300px';

export interface Props extends DOMAttributes {
  onClose: () => void;
}

export const MegaMenu = memo(
  forwardRef<HTMLDivElement, Props>(({ onClose, ...restProps }, ref) => {
    const navTree = useSelector((state) => state.navBarTree);
    const styles = useStyles2(getStyles);
    const location = useLocation();
    const { chrome } = useGrafana();
    const dispatch = useDispatch();
    const state = chrome.useState();
    const [patchPreferences] = usePatchUserPreferencesMutation();
    const pinnedItems = usePinnedItems();
    const navigate = useNavigate();

    const [menus, setMenus] = useState<NavModelItem[]>([]);
    const [activeItem, setActiveItem] = useState<NavModelItem | undefined>();
    const [localStorageValue, setLocalStorageValue] = useState(localStorage.getItem('grafana.navigation'));

    // Remove profile + help from tree
    const navItems = navTree
      .filter((item) => item.id !== 'profile' && item.id !== 'help')
      .map((item) => enrichWithInteractionTracking(item, state.megaMenuDocked));

    //配套兼容menu的 active 样式，url后增加#active=yyyy
    // const regex = /#active=([^?&]+)/;
    // const match = location.hash.match(regex);
    // let hashString = '';
    // if (match && match[1]) {
    //   hashString = match[1];
    // }

    const requestController = useRef({
      currentRequestId: 0,
      isCanceled: false
    });

    function cancelableRequest(fetchFn: any): any {
      const requestId = Date.now() + Math.random();
      requestController.current.isCanceled = false;
      requestController.current.currentRequestId = requestId;

      return new Promise((resolve, reject) => {
        fetchFn()
          .then((response: any) => {
            // 仅处理最新请求的结果
            if (requestController.current.currentRequestId === requestId && !requestController.current.isCanceled) {
              resolve(response);
            } else {
              // 旧请求被取消，忽略结果
              // console.log('旧请求已被取消，忽略结果');
            }
          })
          .catch((error: any) => {
            // 仅处理最新请求的错误
            if (requestController.current.currentRequestId === requestId && !requestController.current.isCanceled) {
              reject(error);
            } else {
              // console.log('旧请求已被取消，忽略错误');
            }
          });
      });
    }

    function cancelCurrentRequest() {
      if (requestController.current.currentRequestId) {
        requestController.current.isCanceled = true;
        // console.log('已手动取消当前请求');
      } else {
        // console.log('当前没有活跃请求，无需取消');
      }
    }

    if (config.featureToggles.pinNavItems) {
      const bookmarksItem = navItems.find((item) => item.id === 'bookmarks');
      if (bookmarksItem) {
        // Add children to the bookmarks section
        bookmarksItem.children = pinnedItems.reduce((acc: NavModelItem[], url) => {
          const item = findByUrl(navItems, url);
          if (!item) {
            return acc;
          }
          const newItem = {
            id: item.id,
            text: item.text,
            url: item.url,
            parentItem: { id: 'bookmarks', text: 'Bookmarks' },
          };
          acc.push(enrichWithInteractionTracking(newItem, state.megaMenuDocked));
          return acc;
        }, []);
      }
    }

    const handleMegaMenu = () => {
      chrome.setMegaMenuOpen(!state.megaMenuOpen);
    };

    const handleDockedMenu = () => {
      chrome.setMegaMenuDocked(!state.megaMenuDocked);
      if (state.megaMenuDocked) {
        chrome.setMegaMenuOpen(false);
      }
    };

    const isPinned = useCallback(
      (url?: string) => {
        if (!url || !pinnedItems?.length) {
          return false;
        }
        return pinnedItems?.includes(url);
      },
      [pinnedItems]
    );

    const onPinItem = (item: NavModelItem) => {
      const url = item.url;
      if (url && config.featureToggles.pinNavItems) {
        const isSaved = isPinned(url);
        const newItems = isSaved ? pinnedItems.filter((i) => url !== i) : [...pinnedItems, url];
        const interactionName = isSaved ? 'grafana_nav_item_unpinned' : 'grafana_nav_item_pinned';
        reportInteraction(interactionName, {
          path: url,
        });
        patchPreferences({
          patchPrefsCmd: {
            navbar: {
              bookmarkUrls: newItems,
            },
          },
        }).then((data) => {
          if (!data.error) {
            dispatch(setBookmark({ item: item, isSaved: !isSaved }));
          }
        });
      }
    };

    const loadMenuData = async (navValue: string | null) => {
      if (navValue != null && navValue !== 'Manage' && navValue !== 'Welcome') {
        const fixedNavTree = window?.grafanaBootData?.fixedNavTree;
        if (fixedNavTree && fixedNavTree[navValue]) {
          const data: NavModelItem[] = await cancelableRequest(() => backendSrv.getFolderMenu(fixedNavTree[navValue].id));
          const newMenus = data.map((item) => enrichWithInteractionTracking(item, state.megaMenuDocked));
          setMenus(newMenus);
          const targetUrl = fixedNavTree[navValue]?.url || '/df';
          navigate(targetUrl);
        } else {
          cancelCurrentRequest();
          setMenus([]);
          if (location.pathname !== '/df') navigate('/df');
        }
      }
    }

    useEffect(() => {
      loadMenuData(localStorageValue);
      const handleStorageChange = () => {
        const navValue = localStorage.getItem('grafana.navigation');
        setLocalStorageValue(navValue);
        loadMenuData(navValue);
      };

      window.addEventListener('localStorageChange', handleStorageChange);

      return () => {
        window.removeEventListener('localStorageChange', handleStorageChange);
      };
    }, []);

    useEffect(() => {
      if (localStorageValue === "Manage" || (localStorageValue === "" && menus.length === 0)) {
        if (menus.length > 0) {
          setMenus([]);
        }
        setActiveItem(getActiveItemByUrl(navItems, location.pathname, true));
      } else {
        const localActiveItem = getActiveItemByUrl(menus, location.pathname, false);
        if (localActiveItem) {
          setActiveItem(localActiveItem);
        }
      }
    }, [location.pathname, localStorageValue, menus])

    return (
      <div data-testid={selectors.components.NavMenu.Menu} ref={ref} {...restProps}>
        <MegaMenuHeader handleDockedMenu={handleDockedMenu} handleMegaMenu={handleMegaMenu} onClose={onClose} />
        <nav className={styles.content}>
          <ScrollContainer height="100%" overflowX="hidden" showScrollIndicators>
            <ul className={styles.itemList} aria-label={t('navigation.megamenu.list-label', 'Navigation')}>
              {localStorageValue === "Manage" && navItems.map((link, index) => (
                <React.Fragment key={`${link.pluginId}-${index}`}>
                  <MegaMenuItem
                    key={link.text}
                    link={link}
                    isPinned={isPinned}
                    onClick={state.megaMenuDocked ? undefined : onClose}
                    activeItem={activeItem}
                    onPin={onPinItem}
                  />
                </React.Fragment>
                
              ))}
              {
                localStorageValue !== "Manage" && menus?.map((menu, index) => (
                  <React.Fragment key={`${menu.id}-${index}`}>
                    <MegaMenuItem
                      key={menu.id}
                      link={menu}
                      isPinned={isPinned}
                      onClick={state.megaMenuDocked ? undefined : onClose}
                      activeItem={activeItem}
                      onPin={onPinItem}
                    />
                  </React.Fragment>
                ))
              }
            </ul>
          </ScrollContainer>
          {shouldRenderInviteUserButton && (
            <div className={styles.inviteNewMemberButton}>
              <InviteUserButton />
            </div>
          )}
        </nav>
      </div>
    );
  })
);

MegaMenu.displayName = 'MegaMenu';

const getStyles = (theme: GrafanaTheme2) => {
  return {
    content: css({
      display: 'flex',
      flexDirection: 'column',
      minHeight: 0,
      flexGrow: 1,
      position: 'relative',
    }),
    mobileHeader: css({
      display: 'flex',
      justifyContent: 'space-between',
      padding: theme.spacing(1, 1, 1, 2),
      borderBottom: `1px solid ${theme.colors.border.weak}`,

      [theme.breakpoints.up('md')]: {
        display: 'none',
      },
    }),
    itemList: css({
      boxSizing: 'border-box',
      display: 'flex',
      flexDirection: 'column',
      listStyleType: 'none',
      padding: theme.spacing(1, 1, 2, 1),
      [theme.breakpoints.up('md')]: {
        width: MENU_WIDTH,
      },
    }),
    inviteNewMemberButton: css({
      display: 'flex',
      padding: theme.spacing(1.5, 1, 1.5, 1),
      borderTop: `1px solid ${theme.colors.border.weak}`,
    }),
    dockMenuButton: css({
      display: 'none',
      position: 'relative',
      top: theme.spacing(1),

      [theme.breakpoints.up('xl')]: {
        display: 'inline-flex',
      },
    }),
  };
};
